package math;

import java.util.HashMap;

public class matSparse
{
    
    private vec2i mSize;
    
    private HashMap<Integer, HashMap<Integer, Double>> mAsRows;
    
    private HashMap<Integer, HashMap<Integer, Double>> mAsCols;
    
    
    public matSparse(int Nrows, int Ncols)
    {
        this.mSize = new vec2i(Nrows, Ncols);
        
        this.mAsRows = new HashMap<>();
        for (int i = 0; i < Nrows; i++)
        {
            this.mAsRows.put(i, new HashMap<Integer, Double>());
        }
        
        this.mAsCols = new HashMap<>();
        for (int j = 0; j < Ncols; j++)
        {
            this.mAsCols.put(j, new HashMap<Integer, Double>());
        }
    }
    
    
    public matSparse copy()
    {
        matSparse copy = new matSparse(this.mSize.x, this.mSize.y);
        for (int row : this.mAsRows.keySet())
        for (int col : this.mAsRows.get(row).keySet())
        {
            copy.set(row, col, this.get(row, col));
        }
        for (int col : this.mAsCols.keySet())
        for (int row : this.mAsCols.get(col).keySet())
        {
            copy.set(row, col, this.get(row, col));
        }
        return copy;
    }
    
    
    public HashMap<Integer, Double> getRow(int i)
    {
        return this.mAsRows.get(i);
    }
    
    public HashMap<Integer, Double> getCol(int j)
    {
        return this.mAsRows.get(j);
    }
    
    
    public double get(vec2i ij)
    {
        HashMap<Integer, Double> rowI = this.mAsRows.get(ij.x);
        Double d = rowI.get(ij.y);
        return (d == null) ? 0.0 : d.doubleValue();
    }
    
    public double get(int i, int j)
    {
        return this.get(new vec2i(i, j));
    }
    
    public double get(int ij)
    {
        return this.get(new vec2i(ij));
    }
    
    
    public void set(vec2i ij, double value)
    {
        if (value == 0.0 || value == -0.0)
        {
            return;
        }
        Double valueD = new Double(value);
        
        HashMap<Integer, Double> rowI = this.mAsRows.get(ij.x);
        Double val = rowI.get(ij.y);
        if (val != null)
        {
            rowI.remove(ij.y);
        }
        rowI.put(ij.y, valueD);
        
        HashMap<Integer, Double> colJ = this.mAsCols.get(ij.y);
        val = colJ.get(ij.x);
        if (val != null)
        {
            colJ.remove(ij.x);
        }
        colJ.put(ij.x, valueD);
    }
    
    public void set(int i, int j, double value)
    {
        this.set(new vec2i(i, j), value);
    }
    
    public void set(int ij, double value)
    {
        this.set(new vec2i(ij), value);
    }
    
    
    public void transpose()
    {
        HashMap<Integer, HashMap<Integer, Double>> asRows = this.mAsRows;
        this.mAsRows = this.mAsCols;
        this.mAsCols = asRows;
        
        int NrowsT = this.mSize.x;
        this.mSize.x = this.mSize.y;
        this.mSize.y = NrowsT;
    }
    
    
    public String toString()
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < this.mSize.x; i++)
        {
            for (int j = 0; j < this.mSize.y; j++)
            {
                double val = get(i, j);
                
                s.append(String.format("%.2f", val));
                s.append(" ");
            }
            s.append("\n");
        }
        return s.toString();
    }
    
    
    public static matSparse mul(matSparse a, matSparse b)
    {
        if (a.mSize.y != b.mSize.x)
        {
            System.err.println("matSparse: Invalid matrix-matrix multiply!");
            return null;
        }
        
        matSparse result = new matSparse(a.mSize.x, b.mSize.y);
        
        for (int i = 0; i < result.mSize.x; i++)
        {
            HashMap<Integer, Double> ArowI = a.mAsRows.get(i);
            
            for (int j = 0; j < result.mSize.y; j++)
            {
                HashMap<Integer, Double> BcolJ = b.mAsCols.get(j);
                
                double dotProd = 0.0;
                for (int AcolJ : ArowI.keySet())
                {
                    if (BcolJ.containsKey(AcolJ)) // AcolJ == BrowI
                    {
                        dotProd += ArowI.get(AcolJ) * BcolJ.get(AcolJ);
                    }
                }
                if (dotProd != 0.0)
                {
                    result.set(i, j, dotProd);
                }
            }
        }
        return result;
    }
    
    
    public static vecN mul(matSparse a, vecN v)
    {
        if (a.mSize.y != v.m.length)
        {
            System.err.println("matSparse: Invalid matrix-vector multiply!");
            return null;
        }
        vecN result = new vecN(a.mSize.x);
        
        for (int i = 0; i < result.m.length; i++)
        {
            result.set(i, dotRowI_Vector(a, i, v));
        }
        return result;
    }
    
    public static double dotRowI_Vector(matSparse a, int i, vecN v)
    {
        HashMap<Integer, Double> ArowI = a.mAsRows.get(i);
        
        double dotProd = 0.0;
        for (int AcolJ : ArowI.keySet())
        {
            dotProd += ArowI.get(AcolJ) * v.get(AcolJ);
        }
        return dotProd;
    }
    
}
